/* Copyright (c) 2022 渝州大数据实验室
 *
 * Lanius is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.yzbdl.lanius.orchestrate.serv.quartz.config;

import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.yzbdl.lanius.orchestrate.serv.constant.TaskPlanSchedulerConstant;
import org.yzbdl.lanius.orchestrate.common.enums.TaskStatusEnum;
import org.yzbdl.lanius.orchestrate.common.vo.task.schedule.AddTaskPlanJobVO;

import java.util.*;

/**
 * task任务创建工具类
 *
 * @author jinchunzhao@yzbdl.ac.cn
 * @date 2022-04-11 14:04
 */
@Component
@Slf4j
public class QuartzJobManager {

    private static QuartzJobManager jobManager;

    /**
     * 任务名称
     */
    private static final String JOB_NAME = "jobName";

    /**
     * 任务分组名称
     */
    private static final String JOB_GROUP_NAME = "jobGroupName";

    /**
     * 触发器
     */
    private static final String TRIGGER = "trigger";

    /**
     * 任务状态
     */
    private static final String JOB_STATUS = "jobStatus";

    /**
     * cron表达式
     */
    private static final String CRON = "cronExpression";

    @Autowired
    private Scheduler scheduler;

    public static QuartzJobManager getInstance() {
        return QuartzJobManager.jobManager;
    }

    /**
     * 创建job
     *
     * @param clazz
     *        任务类
     * @param jobName
     *        任务名称
     * @param jobGroupName
     *        任务分组名称
     * @param cronExpression
     *        cron表达式
     * @throws Exception
     *         任何异常
     */
    public void addJob(Class clazz, String jobName, String jobGroupName, String cronExpression) throws Exception {
        // 启动调度器
        scheduler.start();

        // 构建Job信息
        JobDetail jobDetail = JobBuilder.newJob(((BaseTaskJob)clazz.newInstance()).getClass())
            .withIdentity(jobName, jobGroupName).build();

        // 表达式调度构建器（即任务执行的时间）
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);

        // 使用新的cronExpression表达式构建一个新的trigger
        CronTrigger cronTrigger =
            TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName).withSchedule(cronScheduleBuilder).build();
        scheduler.scheduleJob(jobDetail, cronTrigger);
    }

    /**
     * 创建job
     * <p>
     *     可传参
     * </p>
     *
     * @param clazz
     *        任务类
     * @param jobName
     *        任务名称
     * @param jobGroupName
     *        任务分组名称
     * @param cronExpression
     *        cron表达式
     * @param paramMap
     *        动态参数
     * @throws Exception
     *         任何异常
     */
    public void addJob(Class clazz, String jobName, String jobGroupName, String cronExpression,
        Map<String, Object> paramMap) throws Exception {
        // 启动调度器
        scheduler.start();

        // 构建Job信息
        JobDetail jobDetail = JobBuilder.newJob(clazz)
            .withIdentity(jobName, jobGroupName).build();

        // 表达式调度构建器（即任务执行的时间）
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);

        // 使用新的cronExpression表达式构建一个新的trigger
        CronTrigger cronTrigger =
            TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName).withSchedule(cronScheduleBuilder).build();
        // 获取JobDataMap，写入数据
        cronTrigger.getJobDataMap().putAll(paramMap);

        scheduler.scheduleJob(jobDetail, cronTrigger);
    }

    /**
     * 创建job
     * <p>
     *     可传参，只执行一次任务
     * </p>
     *
     * @param clazz
     *        任务类
     * @param jobName
     *        任务名称
     * @param jobGroupName
     *        任务分组名称
     * @param paramMap
     *        动态参数
     * @throws Exception
     *         任何异常
     */
    public void addJobNoRepeat(Class clazz, String jobName, String jobGroupName, Map<String, Object> paramMap)
        throws Exception {
        // 启动调度器
        scheduler.start();

        // 构建Job信息
        JobDetail jobDetail = JobBuilder.newJob(((BaseTaskJob)clazz.newInstance()).getClass())
            .withIdentity(jobName, jobGroupName).build();

        // 3秒后执行一次
        SimpleScheduleBuilder simpleScheduleBuilder =
            SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).withRepeatCount(0);
        // 创建一个新的trigger
        SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
            .startAt(new Date()).withSchedule(simpleScheduleBuilder).build();

        // 获取JobDataMap，写入数据
        simpleTrigger.getJobDataMap().putAll(paramMap);

        scheduler.scheduleJob(jobDetail, simpleTrigger);
    }

    /**
     * 创建job
     * <p>
     *     只执行一次任务
     * </p>
     *
     * @param clazz
     *        任务类
     * @param jobName
     *        任务名称
     * @param jobGroupName
     *        任务分组名称
     * @throws Exception
     *         任何异常
     */
    public void addJobNoRepeat(Class clazz, String jobName, String jobGroupName) throws Exception {
        // 启动调度器
        scheduler.start();

        // 构建Job信息
        JobDetail jobDetail = JobBuilder.newJob(((BaseTaskJob)clazz.newInstance()).getClass())
            .withIdentity(jobName, jobGroupName).build();

        // 3秒后执行一次
        SimpleScheduleBuilder simpleScheduleBuilder =
            SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).withRepeatCount(0);
        // 创建一个新的trigger
        SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
            .startAt(new Date()).withSchedule(simpleScheduleBuilder).build();

        scheduler.scheduleJob(jobDetail, simpleTrigger);
    }

    /**
     * 暂停Job
     *
     * @param jobName
     *        任务名称
     * @param jobGroupName
     *        任务分组名称
     * @throws SchedulerException
     *         SchedulerException
     */
    public void pauseJob(String jobName, String jobGroupName) throws SchedulerException {
        scheduler.pauseJob(JobKey.jobKey(jobName, jobGroupName));
    }

    /**
     * 恢复Job
     *
     * @param jobName
     *        任务名称
     * @param jobGroupName
     *        任务分组名称
     * @throws SchedulerException
     *         SchedulerException
     */
    public void resumeJob(String jobName, String jobGroupName) throws SchedulerException {
        scheduler.resumeJob(JobKey.jobKey(jobName, jobGroupName));
    }

    /**
     * 立即执行定时任务
     *
     * @param jobName
     *        任务名称
     * @param jobGroupName
     *        任务分组名称
     * @throws SchedulerException
     *         SchedulerException
     */
    public void executeTriggerJob(String jobName, String jobGroupName) throws SchedulerException {
        Trigger trigger = scheduler.getTrigger(TriggerKey.triggerKey(jobName, jobGroupName));
        scheduler.triggerJob(JobKey.jobKey(jobName, jobGroupName), trigger.getJobDataMap());
    }

    /**
     * 更新Job
     * <p>
     *     只更新频率
     * </p>
     *
     * @param jobName
     *        任务名称
     * @param jobGroupName
     *        任务分组名称
     * @param cronExpression
     *        cron表达式
     * @throws Exception
     *         任何异常
     */
    public void updateJob(String jobName, String jobGroupName, String cronExpression) throws Exception {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);

        // 表达式调度构建器
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing();
        CronTrigger cronTrigger = (CronTrigger)scheduler.getTrigger(triggerKey);
        // 使用新的cronExpression表达式构建一个新的trigger
        cronTrigger =
            cronTrigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();

        // 使用新的trigger重新设置job执行
        scheduler.rescheduleJob(triggerKey, cronTrigger);
    }

    /**
     * 更新Job
     * <p>
     *     只更新参数
     * </p>
     *
     * @param jobName
     *        任务名称
     * @param jobGroupName
     *        任务分组名称
     * @param paramMap
     *        参数
     * @throws Exception
     *         任何异常
     */
    public void updateJob(String jobName, String jobGroupName, Map<String, Object> paramMap) throws Exception {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);

        CronTrigger cronTrigger = (CronTrigger)scheduler.getTrigger(triggerKey);

        String cronExpression = cronTrigger.getCronExpression();

        // 表达式调度构建器
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing();
        cronTrigger =
                cronTrigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();

        // 修改参数
        cronTrigger.getJobDataMap().putAll(paramMap);

        // 使用新的trigger重新设置job执行
        scheduler.rescheduleJob(triggerKey, cronTrigger);
    }

    /**
     * 更新Job
     * <p>
     *     更新频率、参数
     * </p>
     *
     * @param jobName
     *        任务名称
     * @param jobGroupName
     *        任务分组名称
     * @param cronExpression
     *        cron表达式
     * @param paramMap
     *        参数
     * @throws Exception
     *         任何异常
     */
    public void updateJob(String jobName, String jobGroupName, String cronExpression, Map<String, Object> paramMap)
        throws Exception {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);

        // 表达式调度构建器
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing();
        CronTrigger cronTrigger = (CronTrigger)scheduler.getTrigger(triggerKey);
        // 使用新的cronExpression表达式构建一个新的trigger
        cronTrigger =
            cronTrigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();

        // 修改参数
        cronTrigger.getJobDataMap().putAll(paramMap);

        // 使用新的trigger重新设置job执行
        scheduler.rescheduleJob(triggerKey, cronTrigger);
    }


    /**
     * 更新Job
     * <p>
     *     更新频率、参数
     * </p>
     *
     * @param jobName
     *        任务名称
     * @param jobGroupName
     *        任务分组名称
     * @param cronExpression
     *        cron表达式
     * @param paramMap
     *        参数
     * @throws Exception
     *         任何异常
     */
    public void updateJobAndStatus(String jobName, String jobGroupName, String cronExpression, Map<String, Object> paramMap,Integer taskStatus)
            throws Exception {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);

        // 表达式调度构建器
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing();
        CronTrigger cronTrigger = (CronTrigger)scheduler.getTrigger(triggerKey);
        // 使用新的cronExpression表达式构建一个新的trigger
        cronTrigger =
                cronTrigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();

        // 修改参数
        cronTrigger.getJobDataMap().putAll(paramMap);

        // 使用新的trigger重新设置job执行
        scheduler.rescheduleJob(triggerKey, cronTrigger);

        if (Objects.equals(TaskStatusEnum.DISABLED.getCode(), taskStatus)){
            this.pauseJob(jobName, jobGroupName);
        }
    }

    /**
     * 删除Job
     *
     * @param jobName
     *        任务名称
     * @param jobGroupName
     *        任务分组名称
     * @throws Exception
     *         任何异常
     */
    public void deleteJob(String jobName, String jobGroupName) throws Exception {
        scheduler.pauseTrigger(TriggerKey.triggerKey(jobName, jobGroupName));
        scheduler.unscheduleJob(TriggerKey.triggerKey(jobName, jobGroupName));
        scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName));
    }

    /**
     * 启动所有的定时任务
     *
     */
    public void startAllJobs() {
        try {
            scheduler.start();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 关闭所有的定时任务
     *
     */
    public void shutdownAllJobs() {
        try {
            if (!scheduler.isShutdown()) {
                scheduler.shutdown();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取都有的任务列表
     *
     * @return
     *        任务列表
     * @throws SchedulerException
     *         SchedulerException
     */
    public List<Map<String, Object>> getAllJobs() throws SchedulerException {
        Set<JobKey> jobKeys = getJobKeys();
        List<Map<String, Object>> jobList = new ArrayList<>();

        for (JobKey jobKey : jobKeys) {
            getJobList(jobKey, jobList);
        }
        return jobList;
    }

    /**
     * 获取指定分组的定时任务
     *
     * @param jobGroupName
     *        任务分组
     * @return
     *        定时任务
     * @throws SchedulerException
     *         SchedulerException
     */
    public List<Map<String, Object>> getJobsByGroup(String jobGroupName) throws SchedulerException {

        Set<JobKey> jobKeys = getJobKeys();
        List<Map<String, Object>> jobList = new ArrayList<>();

        for (JobKey jobKey : jobKeys) {
            if (Objects.equals(jobKey.getGroup(), jobGroupName)) {
                getJobList(jobKey, jobList);
            }
        }
        return jobList;
    }

    /**
     * 获取指定前缀分组的定时任务
     *
     * @param groupPrefix
     *        任务分组前缀
     * @return
     *        定时任务
     * @throws SchedulerException
     *         SchedulerException
     */
    public List<Map<String, Object>> getJobsByGroupStartWith(String groupPrefix) throws SchedulerException {

        Set<JobKey> jobKeys = getJobKeys();
        List<Map<String, Object>> jobList = new ArrayList<>();

        for (JobKey jobKey : jobKeys) {
            if (jobKey.getGroup().startsWith(groupPrefix)) {
                getJobList(jobKey, jobList);
            }
        }
        return jobList;
    }

    /**
     * 获取指定的job
     *
     * @param jobName
     *        任务名称
     * @return
     *        任务信息
     * @throws SchedulerException
     *         SchedulerException
     */
    public Map<String, Object> getJob(String jobName) throws SchedulerException {
        List<Map<String, Object>> allJobs = getAllJobs();
        for (Map<String, Object> job : allJobs) {
            Object jobNameObj = job.get(JOB_NAME);
            String jobNameStr = Objects.isNull(jobNameObj) ? null : jobNameObj.toString();
            if (Objects.equals(jobNameStr, jobName)) {
                return job;
            }
        }
        return new HashMap<>();

    }

    /**
     * 构建任务编排定时任务参数
     *
     * @param taskPlanJobVO
     *        实体
     * @return
     *        参数Map
     */
    public Map<String,Object> buildTaskPlanJobDataMap(AddTaskPlanJobVO taskPlanJobVO){
        // 定时任务上下文传递参数
        Map<String, Object> paramMap = Maps.newHashMap();

        paramMap.put(TaskPlanSchedulerConstant.TASK_PLAN_ID, taskPlanJobVO.getTaskPlanId());
        paramMap.put(TaskPlanSchedulerConstant.TASK_PLAN_GROUP_ID, taskPlanJobVO.getTaskPlanGroupId());
        paramMap.put(TaskPlanSchedulerConstant.TASK_PLAN_IS_ASYNC, taskPlanJobVO.getAsync());
        paramMap.put(TaskPlanSchedulerConstant.TASK_PLAN_NODE_ID, taskPlanJobVO.getNodeId());
        paramMap.put(TaskPlanSchedulerConstant.TASK_PLAN_INCR_LOG, taskPlanJobVO.getIsIncrementalLog());

        return paramMap;
    }

    /**
     * 获取job对象集合
     *
     * @return
     *        job对象集合
     * @throws SchedulerException
     *         SchedulerException
     */
    private Set<JobKey> getJobKeys() throws SchedulerException {
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        return scheduler.getJobKeys(matcher);
    }

    /**
     * 收集任务集合
     *
     * @param jobKey
     *        表明Job身份的一个对象
     * @param jobList
     *        任务列表集合
     * @throws SchedulerException
     *         SchedulerException
     */
    private void getJobList(JobKey jobKey, List<Map<String, Object>> jobList) throws SchedulerException {
        String jobKeyName = jobKey.getName();
        String jobKeyGroup = jobKey.getGroup();
        List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
        for (Trigger trigger : triggers) {
            Map<String, Object> jobMap = new HashMap<>();
            jobMap.put(JOB_NAME, jobKeyName);
            jobMap.put(JOB_GROUP_NAME, jobKeyGroup);
            jobMap.put(TRIGGER, trigger.getKey());
            Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
            jobMap.put(JOB_STATUS, triggerState.name());
            if (trigger instanceof CronTrigger) {
                CronTrigger cronTrigger = (CronTrigger)trigger;
                String cronExpression = cronTrigger.getCronExpression();
                jobMap.put(CRON, cronExpression);
            }
            jobList.add(jobMap);
        }
    }

}
